The following code was ran with an peculiar initial state in place of the comment # unknown starting state
use std::sync::{Arc, atomic::AtomicBool};
const GRID_SIZE: usize = 29;
struct Mem([bool; GRID_SIZE * GRID_SIZE]);
impl Mem {
fn new(grid: [bool; GRID_SIZE * GRID_SIZE]) -> Self {
Self(grid)
}
fn get(&self, x: u8, y: u8) -> bool {
if y >= GRID_SIZE as u8 || x >= GRID_SIZE as u8 {
return false;
}
self.0[y as usize * GRID_SIZE + x as usize]
}
fn set(&mut self, x: u8, y: u8, val: bool) {
self.0[y as usize * GRID_SIZE + x as usize] = val;
}
}
impl Default for Mem {
fn default() -> Self {
Self::new([false; GRID_SIZE * GRID_SIZE])
}
}
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
enum Dir {
Up,
Right,
Down,
Left,
}
struct Langton {
x: u8,
y: u8,
dir: Dir,
}
impl Langton {
fn new(x: u8, y: u8, dir: Dir) -> Self {
Self { x, y, dir }
}
fn step(&mut self) {
match self.dir {
Dir::Up => {
self.y = if self.y == 0 {
GRID_SIZE as u8 - 1
} else {
self.y - 1
}
}
Dir::Down => self.y = (self.y + 1) % GRID_SIZE as u8,
Dir::Left => {
self.x = if self.x == 0 {
GRID_SIZE as u8 - 1
} else {
self.x - 1
}
}
Dir::Right => self.x = (self.x + 1) % GRID_SIZE as u8,
}
}
fn turn_right(&mut self) {
self.dir = match self.dir {
Dir::Up => Dir::Right,
Dir::Right => Dir::Down,
Dir::Down => Dir::Left,
Dir::Left => Dir::Up,
}
}
fn turn_left(&mut self) {
self.dir = match self.dir {
Dir::Right => Dir::Up,
Dir::Down => Dir::Right,
Dir::Left => Dir::Down,
Dir::Up => Dir::Left,
}
}
}
fn display(mem: &Mem) {
let mut res = String::new();
res.push_str("\x1b[H");
for y in 0..=(GRID_SIZE as u8 / 2) {
let mut line = String::new();
for x in 0..(GRID_SIZE as u8) {
if mem.get(x, y * 2) {
line.push_str("\x1b[107m");
} else {
line.push_str("\x1b[40m");
}
if mem.get(x, y * 2 + 1) {
line.push_str("\x1b[97m");
} else {
line.push_str("\x1b[30m");
}
line.push('\u{2584}');
}
line.push('\n');
res.push_str(&line);
}
println!("{}", res);
}
fn main() {
let mut running = Arc::new(AtomicBool::new(true));
let mut r = running.clone();
println!("\x1b[2J");
let mut mem = Mem::new([
# unkown starting state
]);
let mut langton = Langton::new(GRID_SIZE as u8 / 2, GRID_SIZE as u8 / 2, Dir::Right);
let mut i = 0;
while running.load(std::sync::atomic::Ordering::SeqCst) {
i += 1;
if mem.get(langton.x, langton.y) {
langton.turn_left();
mem.set(langton.x, langton.y, false);
} else {
langton.turn_right();
mem.set(langton.x, langton.y, true);
};
langton.step();
display(&mem);
println!("\x1b[0m{i}");
if i == 3000 {
break;
}
}
println!("\x1b[2J");
display(&mem);
println!("\x1b[0m{i}");
println!("{:?}", langton.dir);
println!("{};{}", langton.x, langton.y);
}
It's output after finishing looked like this:
Are you able to reverse the execution of the program so that you can see what the initial state was?
The code can also be found here